#pragma once

#include <iostream>
#include "Socked.hpp"
#include <sys/time.h>
#include <poll.h>

using namespace std;

static const uint16_t defaultport = 8080;
int defaultfd = -1;
int non_event = 0;
class PollServer
{
public:
    PollServer(const uint16_t port = defaultport)
        : _port(port)
    {
        for (auto c : _event_fds)
        {
            c.fd = defaultfd;
            c.events = 0;
            c.revents = 0;
        }
    }
    ~PollServer()
    {
        _listensock.Close();
    }

    bool Init()
    {
        _listensock.Socket(); // 创建套接字
        _listensock.Bind(_port);
        _listensock.Listen();

        return true;
    }

    void HandlerEvent()
    {
        for (int i = 0; i < 64; i++)
        {
            int fd = _event_fds[i].fd;
            if (fd == -1)
                continue;
            if (_event_fds[i].revents & POLLIN) // 资源已就绪
            {
                if (fd == _listensock.Fd()) // 监听套接字就绪
                {
                    string clientip;
                    uint16_t clientport = 0;
                    int sock = _listensock.Accept(&clientip, &clientport); // 这一次连接，不会阻塞
                    if (sock < 0)
                    {
                        lg(Error, "accepet error");
                        return;
                    }
                    lg(Info, "accept success, %s: %d, fd: %d", clientip.c_str(), clientport, sock);

                    // 不能直接进行read、write
                    // 原因举例read，此时的缓冲区可能没有数据，导致read阻塞在这里，进而导致HandlerEvent函数无法返回
                    // 所以资源是否就绪的问题，统统交给上面的select函数处理（三个位图参数）

                    // 正解:将获取的新连接sock交给select等待
                    // 将 sock 添加到辅助数组即可，因为辅助数组会在while中不断刷新，并且交给select
                    for (int i = 1; i <= 64; i++) // 0已被listensock占用
                    {
                        if (_event_fds[i].fd != -1)
                        {
                            if (i == 64)
                            {
                                // 没有空位了
                                lg(Warning, "Poll is Full, already close fd: %d\n", sock);
                                close(sock);
                                // 数组指针：构造函数时使用malloc分配，扩容使用realloc
                                // vector: 使用push_back(0, POLLIN, 0),扩容使用resize
                                break;
                            }
                            continue;
                        }
                        else
                        {
                            _event_fds[i].fd = sock;
                            _event_fds[i].events = POLLIN;
                            //lg(Info, "已转接sock: %d 至辅助数组\n", sock);
                            break;
                        }
                    }

                    // int pos = 1;
                    // for (; pos < 1024; pos++) // 第二个循环
                    // {
                    //     if (_fd_array[pos] != -1)
                    //         continue;
                    //     else
                    //         break;
                    // }
                    // if (pos == 1024)
                    // {
                    //     lg(Warning, "server is full, close %d now!", sock);
                    //     close(sock);
                    // }
                    // else
                    // {
                    //     _fd_array[pos] = sock;
                    // }
                }
                else // 说明执行的是其余文件描述符资源
                {
                    char buffer[1024];
                    ssize_t n = read(fd, &buffer, sizeof(buffer) - 1);
                    if (n > 0)
                    {
                        buffer[n] = 0;
                        cout << "get a message: " << buffer << endl;
                    }
                    else if (n == 0)
                    {
                        // 对方关闭文件描述符
                        lg(Info, "client quit, me too, fd is: %d", fd);

                        // 这里也是poll区别于select：结构体数组的这个【数组】可以定义为指针or vector进行扩容，而不是简单的关闭腾出空间了
                        close(fd);
                        // 数组指针：构造函数时使用malloc分配，扩容使用realloc
                        // vector: 使用push_back(0, POLLIN, 0),扩容使用resize
                    }
                    else
                    {
                        lg(Warning, "read error\n");
                        close(fd);
                        _event_fds[i].fd = -1;
                    }
                }
            }
        }
    }

    void Start()
    {
        _event_fds[0].fd = _listensock.Fd(); // 第一个默认为listen套接字
        _event_fds[0].events = POLLIN;       // 用户告诉内核，只关心这个套接字的读事件是否就绪

        int timeout = 3000; // 3s
        while (1)
        {
            int n = poll(_event_fds, 64, timeout);
            switch (n) // 对poll返回值进行判断
            {
            case 0: // 超时
                cout << "time out:... " << endl;
                break;
            case -1: // 错误
                cerr << "poll error" << endl;
                break;
            default:                               // 有事件就绪
                cout << "get a new link!" << endl; // 这里会刷屏【因为select需要等待将该连接取走，不取走 select会一直通知你，handlerEvent处理】
                HandlerEvent();
                break;
            }
        }
    }

private:
    Sock _listensock;
    uint16_t _port;

    // 指针数组？
    struct pollfd _event_fds[64]; // 时间文件描述符集合
};